﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Speech.Recognition;
using Microsoft.Speech.AudioFormat;
using Microsoft.Kinect;

namespace KinectMenagerNamespace
{
    public class KinectSpeechRecognizer : IDisposable
    {
        SpeechRecognitionEngine engine;

        public event EventHandler OK;
        public event EventHandler MOVE;
        public event EventHandler DOWN;

        public KinectSpeechRecognizer()
        {
            RecognizerInfo ri = GetKinectRecognizer();

            engine = new SpeechRecognitionEngine(ri.Id);

            var directions = new Choices();
            directions.Add(new SemanticResultValue("ok", "OK"));
            directions.Add(new SemanticResultValue("stop", "OK"));
            directions.Add(new SemanticResultValue("move", "MOVE"));
            directions.Add(new SemanticResultValue("down", "DOWN"));
            directions.Add(new SemanticResultValue("next", "DOWN"));
               
            var gb = new GrammarBuilder { Culture = ri.Culture };
            gb.Append(directions);
               
            var g = new Grammar(gb);

            engine.LoadGrammar(g);

            engine.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(engine_SpeechRecognized); 
        }

        internal void SetAudioSource(KinectAudioSource source)
        {
            engine.SetInputToAudioStream(source.Start(), new SpeechAudioFormatInfo(EncodingFormat.Pcm, 16000, 16, 1, 32000, 2, null));
            engine.RecognizeAsync(RecognizeMode.Multiple);
        }

        void engine_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            const double ConfidenceThreshold = 0.3;

            if (e.Result.Confidence >= ConfidenceThreshold)
            {
                switch (e.Result.Semantics.Value.ToString())
                {
                    case "OK":
                        {
                            if (OK != null)
                            {
                                OK.Invoke(this, new EventArgs());
                            }
                            break;
                        }
                    case "MOVE":
                        {
                            if (MOVE != null)
                            {
                                MOVE.Invoke(this, new EventArgs());
                            }
                            break;
                        }
                    case "DOWN":
                        {
                            if (DOWN != null)
                            {
                                DOWN.Invoke(this, new EventArgs());
                            }
                            break;
                        }
                }
            }
        }

        private static RecognizerInfo GetKinectRecognizer()
        {
            foreach (RecognizerInfo recognizer in SpeechRecognitionEngine.InstalledRecognizers())
            {
                string value;
                recognizer.AdditionalInfo.TryGetValue("Kinect", out value);
                if ("True".Equals(value, StringComparison.OrdinalIgnoreCase) && "en-US".Equals(recognizer.Culture.Name, StringComparison.OrdinalIgnoreCase))
                {
                    return recognizer;
                }
            }

            return null;
        }

        public void Dispose()
        {
            engine.SpeechRecognized -= engine_SpeechRecognized;
            engine.RecognizeAsyncStop();
        }
    }
}
